package com.gitee.wsl.result.process

import com.gitee.wsl.result.StateEntryResult
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map

/**
 * [collect] is an extension function which takes in
 * a loader which helps in showing the progressBar for each execution
 * and onError lambda gets triggered if any exception is thrown.
 *
 * Please refer to our documentation for more [details](https://kasem-sm.github.io/SlimeKT/guide/interactors/)
 */
suspend fun <T,R,S> Flow<StateEntryResult<T, R,S>>.collect(
    loader: ObservableLoader = ObservableLoader(),
    onError: suspend (String?) -> Unit,
    onSuccess: suspend (T?) -> Unit,
) = collect { stage ->
    when {
        stage.isStateValue -> loader.start()
        stage.isOk->{
            onSuccess(stage.value)
            loader.stop()
        }
        stage.isErr -> {
            onError(stage.error.toString())
            loader.stop()
        }
    }
}

suspend fun <T,R,S> Flow<StateEntryResult<T, R,S>>.collect(
    onError: suspend (String?) -> Unit={},
    onState: suspend (S?) ->Unit={},
    onSuccess: suspend (T?) -> Unit ,
) = collect { stage ->
    when {
        stage.isStateValue ->onState(stage.component3())
        stage.isOk -> onSuccess(stage.component1())
        stage.isErr -> onError(stage.component2()?.toString())
        else -> Unit
    }
}

/**
 * Observable Loader exposes a [flow] of type Boolean which returns true
 * when the count is greater than 0.
 * It consists of a `[start]` function which increments the counter
 * and `[stop]` function which decrements the counter.
 *
 * Please refer to our documentation for more [details](https://kasem-sm.github.io/SlimeKT/guide/observable_loader/)
 */
class ObservableLoader {
    private val count = atomic(0)
    private val loadingState = MutableStateFlow(count.value)

    val flow: Flow<Boolean>
        get() = loadingState.map { it > 0 }.distinctUntilChanged()

    val isLoading get() = count.value > 0

    fun start() {
        loadingState.value = count.incrementAndGet()
    }

    fun stop() {
        loadingState.value = count.decrementAndGet()
    }
}